package io.zbus.data.api;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;

/**
 * 
 * Database operations, mainly applied in Javascript environment
 * 
 * @author Rushmore
 *
 */
public interface Db {  
	/**
	 * Execute raw select SQL
	 * @param sql SQL string
	 * @param args parameters in SQL
	 * @return list of row(map)
	 * 
	 * @since 1.0
	 */
	List<Map<String, Object>> queryList(String sql, Object[] args); 
	List<Map<String, Object>> queryList(String sql);  
	
	List<Map<String, Object>> queryList(String sql, Object[] args, Object jsonWhere); 
	List<Map<String, Object>> queryList(String sql, Object jsonWhere); 
	
	<T> List<T> queryList(Class<T> type, String sql, Object[] args); 
	<T> List<T> queryList(Class<T> type, String sql); 
	
	/**
	 * Execute raw select SQL
	 * @param sql SQL string
	 * @param args parameters in SQL
	 * @return list of row(map)
	 * 
	 * @since 1.0
	 */
	List<Map<String, Object>> queryPage(String sql, int page, int size,  Object[] args); 
	List<Map<String, Object>> queryPage(String sql, int page, int size); 
	
	List<Map<String, Object>> queryPage(String sql, int page, int size,  Object[] args, Object jsonWhere); 
	List<Map<String, Object>> queryPage(String sql, int page, int size, Object jsonWhere);
	
	<T> List<T> queryPage(Class<T> type, String sql, int page, int size,  Object[] args); 
	<T> List<T> queryPage(Class<T> type, String sql, int page, int size); 

	/**
	 * Select one row via SQL
	 * @param sql SQL string
	 * @param args parameters in SQL
	 * @return a row(map)
	 
	 * @since 1.0
	 */
    Map<String, Object> queryOne(String sql, Object[] args);   
    Map<String, Object> queryOne(String sql);   
    
    Map<String, Object> queryOne(String sql, Object[] args, Object jsonWhere);  
    Map<String, Object> queryOne(String sql, Object jsonWhere);
    
    <T> T queryOne(Class<T> type, String sql, Object[] args);  
    <T> T queryOne(Class<T> type, String sql);  
    
    
    /**
     * Select single column value from db
     * @param sql SQL string
     * @param args parameters in SQL
     * @return value should be converted to desired target type
     */
    Object queryObject(String sql, Object[] args);  
    Object queryObject(String sql);  
    
    Object queryObject(String sql, Object[] args, Object jsonWhere);  
    Object queryObject(String sql, Object jsonWhere);  
    
    <T> T queryObject(Class<T> type, String sql, Object[] args);  
    <T> T queryObject(Class<T> type, String sql);   
    
 
    
    /**  
     * 
     * Single table query
     * @param table table name
     * @param jsonWhere  
     * @return
     */
    List<Map<String, Object>> select(String table, Integer page, Integer size, Object jsonWhere);  
    List<Map<String, Object>> select(String table, Integer page, Integer size);
    List<Map<String, Object>> select(String table, Object jsonWhere);
    List<Map<String, Object>> select(String table);
    
    <T> List<T> select(Class<T> type, String table, Integer page, Integer size, Object jsonWhere);
    <T> List<T> select(Class<T> type, String table, Object jsonWhere);
    <T> List<T> select(Class<T> type, String table, Integer page, Integer size);
    <T> List<T> select(Class<T> type, String table);
    
    /**
     * Insert a map into table, optional by key
     * @param table table name
     * @param record a row map
     * @param key key column name
     * @return affected row count
     */ 
    <T> int insert(String table, T record);   
    
    
    /**
     * If all primary key populated, try to update, otherwise create
     * 
     * @param table
     * @param record
     * @return
     */
    int save(String table, Map<String, Object> record);
    <T> int save(String table, T record);
    
    /**
     * Update table
     * 
     * @param table table name
     * @param record a row map
     * @param key array of columns as condition to update, empty to use default primary keys
     * @return
     */ 
    <T> int update(String table, T record, String where, Object[] args); 
    <T> int update(String table, T record, String where);  
    <T> int update(String table, T record, Object jsonWhere);   
    <T> int update(String table, T record);  
    
    /**
     * Remove table records
     * 
     * @param table table name
     * @param where question marked query string
     * @param args for question marks in where
     * @return affected row count
     * 
     */
    int remove(String table, String where, Object[] args); 
    int remove(String table, String where); 
    int remove(String table, Object jsonWhere); 
    
    /**
     * Execute any SQL with question marked parameters
     * 
	 * @param sql SQL string with question marked parameter
	 * @param args parameters in SQL
     * @return affected rows
     */
    int execute(String sql, Object[] args);   
    int execute(String sql); 
    
    int execute(String sql, Object[] args, Object jsonWhere);  
    int execute(String sql, Object jsonWhere);  
    
    
    //Batch operations
    /**
     * Batch insert not support generated key back.
     * 
     * https://jira.spring.io/browse/SPR-1836
     * This is because the JDBC spec doesn't guarantee that the generated keys will be made available after a batch update.
     * @param table
     * @param record
     * @return
     */ 
    <T> List<Integer> batchInsert(String table, List<T> record);  
    List<Integer> batchUpdate(String sql, Object batchArgs); //List<Object[]> batchArgs 
    List<Integer> batchUpdate(String sql, List<Object[]> batchArgs); 
    
    // Cascades
    void linkQuery(Object data, Object query);
    void linkQuery(Object record, LinkQuery query);
    void linkQuery(List<Object> records, LinkQuery query);     
    
    //Transactions
    void tx(Runnable runner);    

	public static class LinkQuery {
		/**
		 * Link-property created location
		 */
		public String path;     
		/**
		 * Link key in data, location: parent(location).key
		 */
		public String key;
		
		/**
		 * Any select SQL with args(question mark?)
		 */
		public String sql;    
		  
		/**
		 * link join key to path's data value, including table name(alias) if specified, 
		 */
		public String linkColumn;  
		
		/**
		 * if question marks in SQL, args required.
		 */
		public List<Object> args = new ArrayList<>(); 
		/**
		 * whether this link is ManyToOne or OneToOne, group key still required since in batch manner
		 */
		public Boolean isOne; 
	}  
}
